/**
 * 实现一个proxy pool，提供可用的proxy
 */
const EventEmitter = require('events').EventEmitter;
const request = require("request");
const { URL } = require('url');
const requestAsync = require("./lib/utils/http_util.js");
const ProxyConsts = require('./lib/proxy_consts.js');
const ProxySource = require("./lib/proxy_source.js");
const ProxySourceXici = require("./lib/proxy_source_xici.js");
const debug = require('debug')('proxy-pool');

function ProxyPool() {
  this._isRunning = false;
  this._sourceMap = new Map();  // source数据
  this._proxyList = [];         // 已经验证过的数据
  this._proxySet = new Set();   // 已经验证过的数据集合

  this._interval = 60 * 1000;
  this._startTime = 0;  // 开始时间(ms)
  this._endTime = 0;    // 结束时间(ms)
  this._isProxyEmpty = true;  // 是否为空数据
  this._eventEmitter = new EventEmitter();
}

/**
 * 启动proxy pool
 */
ProxyPool.prototype.start = function() {
  if(this._isRunning) {
    return true;
  }
  debug("启动pool-proxy");
  let source = null;
  this.addSource(ProxySourceXici.name, ProxySourceXici.func);
  // this.addSource(ProxySourceXici.name, ProxySourceXici.func, ProxySourceXici.data);
  for(let [key, source] of this._sourceMap) {
    source.start();
  }

  this._isRunning = true;
  // 启动定时检测
  this._tick();
}

/**
 * 注册拥有proxy事件
 */
ProxyPool.prototype.on = function(event, callback) {
  this._eventEmitter.on(event, callback);
}

/**
 *  转化出proxy url string
 */
ProxyPool.prototype.proxyUrl = function(proxy) {
  return proxy.protocol + "://" + proxy.ip + ":" + proxy.port;
}

/**
 * 获取一个proxy
 * @param (string) type 采用何种方式获取，best最高得分, rand随机
 * @returns object 包含proxy信息 {ip, port, protocol, source, anonymity}
 */
ProxyPool.prototype.get = function(url, type) {
  const myURL = new URL(url);
  let protocol = myURL.protocol;
  type = type || "best";
  if(!this._proxyList) {
    return null;
  }
  let proxy = null;
  if(type == "best") {
    let max = -1;
    let index = -1;
    for(let i = 0; i < this._proxyList.length; ++i) {
      if(this._proxyList[i].score > max && this._proxyList[i].protocol == protocol) {
        max = this._proxyList[i].score;
        index = i;
      }
    }
    if(index != -1) {
      proxy = this._proxyList[index];
    }
  } else if(type == "rand") {
    let list2 = this._proxyList.filter(elem => elem.protocol == protocol);
    if(lists2.length > 0) {
      proxy = lists2[Math.floor(Math.random() * this._proxyList.length)];
    }
  }
  if(proxy) {
    return this.proxyUrl(proxy);
  }
  return null;
}

/**
 * 获取所有的proxy
 */
ProxyPool.prototype.list = function() {
  return this._proxyList;
}

/**
 * 获取所有的source list
 */
ProxyPool.prototype.sourceList = function() {
  return Array.from(this._sourceMap.keys());
}

/**
 * 添加source
 * @param (string) name source的名称
 * @param (function) srcFunc 对应的获取分析函数
 */
ProxyPool.prototype.addSource = function(name, srcFunc) {
  debug("添加source:", name);
  if(this._sourceMap.has(name)) {
    return true;
  }
  let source = new ProxySource(name, srcFunc, this);
  if(source) {
    this._sourceMap.set(name, source);
    return true;
  }
  return false;
}

/**
 * 检测原始的代理是否可用
 */
ProxyPool.prototype.checkRoughProxys = async function(list) {
  for(let i = 0; i < list.length; ++i) {
    let [isValid, score] = await this._checkProxyItem(list[i]);
    if(isValid) {
      list[i].score = score;
      this._addProxy(list[i]);
    }
  }
}

/**********************************
 * internal API
 **********************************/

/**
 * 检测单个代理
 */
const probeUrl = "http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js";
// const probeUrl = "https://api.ipify.org/?format=json";
ProxyPool.prototype._checkProxyItem = async function (proxy) {
  // debug("检测proxy:", proxy.ip, "port", proxy.port);
  try {
    let timeout = 5000;
    let reqeustOpts = {
      url: probeUrl,
      timeout: timeout,
      proxy: this.proxyUrl(proxy),
      headers: ProxyConsts.HTTP_HEADERS
    };
    let startTime = +new Date;
    let ret = await requestAsync(reqeustOpts);
    let useTime = +new Date - startTime;
    let score = Math.max(0, Math.floor((1 - useTime/timeout) * 100));
    debug("proxy:", proxy.ip, "port", proxy.port, "得分:", score);
    return [true, score];
  } catch (error) {
    // debug("proxy不可用:", proxy.ip);
    return [false, 0];
  }
}

/**
 * 添加可用的proxy，proxy source调用
 */
ProxyPool.prototype._addProxy = function(proxy) {
  // debug("添加proxy:", proxy);
  if(Array.isArray(proxy)) {
    proxy.forEach(elem => {
      this._addProxySingle(elem);
    });
  } else {
    this._addProxySingle(proxy);
  }
}

ProxyPool.prototype._addProxySingle = function(proxy) {
  let key = proxy.ip + ":" + proxy.port;
  if(!this._proxySet.has(key)) {
    this._proxySet.add(key);
    this._proxyList.push(proxy);
    this._eventEmitter.emit("has_proxy", proxy);
  }
}

/**
 * tick 逻辑
 */
ProxyPool.prototype._tick = async function () {
  debug("proxy pool执行tick逻辑");
  if (!this._isRunning) {
    return false;
  }
  this._startTime = +new Date;
  await this._tickWork();
  this._endTime = +new Date;
  let timeuse = this._endTime - this._startTime;
  let timeInterval = Math.max(this._interval - timeuse, 500);
  // logger.debug("%s下次tick 时间：", this.name, timeInterval, " start:", this._startTime, " end:", this._endTime);
  setTimeout(this._tick.bind(this), timeInterval);
}

/**
 * 执行具体的tick逻辑
 */
ProxyPool.prototype._tickWork = async function() {
  try {
    for(let i = 0; i < this._proxyList.length; ++i) {
      let [isValid, score] = await this._checkProxyItem(this._proxyList[i]);
      if(isValid) {
        this._proxyList[i].score = score;
      } else {
        this._proxyList.splice(i, 1);
      }
    }
    debug("可用的proxy数目:", this._proxyList.length, this._proxyList);
  } catch (error) {
    logger.error("proxy pool执行数据检测出错");
  }
  return true;
}

exports = module.exports = ProxyPool;